package cn.com.cfae.iras.doc.analyze.parse;

import cn.com.cfae.iras.doc.analyze.parse.extractor.MTableExtractor;
import cn.com.cfae.iras.doc.analyze.parse.kv.KVText;
import cn.com.cfae.iras.doc.analyze.parse.kv.TextLayout;
import cn.com.cfae.iras.doc.analyze.parse.model.*;
import cn.com.cfae.iras.doc.analyze.parse.model.table.TableItem;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.corpus.io.FileIOAdapter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class PDFMTableParser extends PDFBaseParser {

    private DocumentModel documentModel = null;

    private TableUtil tableUtil = TableUtil.getInstance();

    private RegexUtil regexUtil;

    private static final Logger logger = LoggerFactory.getLogger(PDFMTableParser.class);

    public PDFMTableParser() {

    }

    public DocumentModel parse(File pdfFile, TableType tableType) {
        return parse(pdfFile, tableType, null);
    }

    @Override
    /**
     * 声明：
     * 1、PDF默认从第一页开始
     */
    public DocumentModel parse(File pdfFile, TableType tableType, IndustryType industryType) {
        logger.info("开始分析PDF，表格类型：{}，文件路径：{}。", tableType.getName(), pdfFile.getAbsolutePath());
        long t1 = System.currentTimeMillis();
        PDDocument pdDocument = loadPDDocument(pdfFile);
        HanLP.Config.IOAdapter = new FileIOAdapter();
        regexUtil = new RegexUtil();
        documentModel = new DocumentModel();
        documentModel.setPdfFilePath(pdfFile.getAbsolutePath());
        documentModel.setChapterModels(new ArrayList<>());
        documentModel.setActualChapterModelList(new ArrayList<>());
        documentModel.setTotalPageNumber(pdDocument.getNumberOfPages());
        documentModel.setIndustryType(industryType);
        documentModel.setNoThousandSeparatorList(new ArrayList<>());
        if (documentModel.getTotalPageNumber() <= 0) {
            logger.error("分析PDF结构，PDF文件：{}，文件为空，请确认！！！", pdfFile.getAbsolutePath());
            return documentModel;
        }

        setBaseTable(tableUtil.initTable(tableType));
        ChapterModel chapterModel = null;
        ChapterModel actualChapterModel = null;
        PageModel pageModel = null;
        ChapterModel previousChapterModel = null;
        List<String> pageDataList = null;
        PDPage pdPage = pdDocument.getPage(1);
        double width = pdPage.getCropBox().getWidth();
        double height = pdPage.getCropBox().getHeight();
        //默认抽取的页码边界，过滤掉页码
        PageArea defaultPageArea = new PageArea(50, 10, height, width);
        try {
            pageDataList = extractPdfByText(new String[]{pdfFile.getAbsolutePath(), "-t", "-p", (String.valueOf(1) + '-' + String.valueOf(documentModel.getTotalPageNumber())), "-a", defaultPageArea.getLayout(), "-f", "JSON"});
        } catch (Exception e) {
            logger.error("分析PDF结构，分析PDF文件异常，pdf文件：{}。", pdfFile.getAbsolutePath(), e.getCause());
            return documentModel;
        }

        List<List<String>> pageTableDataList = null;
        try {
            pageTableDataList = extractPdfAllTables(new String[]{pdfFile.getAbsolutePath(), "-l", "-p", (String.valueOf(1) + '-' + String.valueOf(documentModel.getTotalPageNumber())), "-a", defaultPageArea.getLayout(), "-f", "JSON"});
        } catch (Exception e) {
            logger.error("分析PDF结构，分析PDF文件异常，pdf文件：{}。", pdfFile.getAbsolutePath(), e.getCause());
            return documentModel;
        }

        for (int i = 0; i < pageDataList.size(); i++) {
            String jsonTextString = pageDataList.get(i);
            logger.info("分析PDF结构，正在处理文件：{}，总页码：{}，当前处理页码：{}，页面内容：{}。", pdfFile.getName(), documentModel.getTotalPageNumber(), (i + 1), jsonTextString);
            JsonElement jsonTextData = new JsonParser().parse(jsonTextString);
            pageModel = new PageModel();
            pageModel.setTableDataList(pageTableDataList.get(i));
            pageModel.setJsonTextData(jsonTextData);
            int pageNumber = (i + 1);
            if (i == 0) {
                if (!regexUtil.isProspectus2(jsonTextString)) {
                    throw new RuntimeException("该文档疑似非募集说明书！");
                }
            }
            try {
                pageModel.setPageNumber(pageNumber);
                if (i == 0) {
                    logger.info("分析PDF结构，当前页码为第{}页即扉页，默认为M-0/BM-0章节。", pageNumber);
                    chapterModel = new ChapterModel();
                    chapterModel.setStartPage(pageNumber);
                    chapterModel.setChapterTitle("目录");
                    chapterModel.setTableItem(getBaseTable().getTableItemList().get(0));
                    chapterModel.setCatalogModels(new ArrayList<>());
                    chapterModel.setBottomPageNum(getPageModelBottomPageNum(pageModel));
                    documentModel.addChapterModel(chapterModel);
                    {
                        actualChapterModel = new ChapterModel();
                        actualChapterModel.setChapterTitle(chapterModel.getChapterTitle());
                        actualChapterModel.setBottomPageNum(chapterModel.getBottomPageNum());
                        documentModel.addActualChapterModel(actualChapterModel);
                    }
                } else if (i == 1) {
                    logger.info("分析PDF结构，当前页码：{}，即重要提示，默认为M-0/BM-0章节。", pageModel.getPageNumber());
                } else {
                    JsonElement pageTextData = pageModel.getJsonTextData();
                    JsonObject jsonObject = pageTextData.getAsJsonArray().get(0).getAsJsonObject();
                    List<TableArea> tableAreaList = recognizePageTableRegion(pageModel);
                    JsonArray dataArray = jsonObject.get("data").getAsJsonArray();
                    for (int j = 0; j < dataArray.size(); j++) {
                        JsonArray lineArray = dataArray.get(j).getAsJsonArray();
                        String combineLine = combineTextJsonArray(lineArray);
                        if (StringUtils.isEmpty(combineLine)) {
                            continue;
                        }
                        if (regexUtil.isCatalogLevel1(combineLine)) {
                            logger.info("分析PDF结构，发现一级目录结构内容，文本内容：{}。", combineLine);
                            CatalogModel catalogModel = new CatalogModel();
                            catalogModel.setCatalogSeqNo(pageNumber);
                            catalogModel.setCatalogName(combineLine);
                            chapterModel.getCatalogModels().add(catalogModel);
                            //页码
                            //TODO 默认获取0，可能会出现空值情况，后续再修改
                            catalogModel.setTextLayout(createKVTextLayout(lineArray.get(0).getAsJsonObject(), pageNumber));
                            catalogModel.setCatalogIndex(regexUtil.extractPageNumber(combineLine));
                            continue;
                        }
                        if (regexUtil.isCatalogLevel2(combineLine) && previousChapterModel == null) {
                            logger.info("分析PDF结构，发现二级目录结构内容，文本内容：{}。", combineLine);
                            CatalogModel catalogModel = new CatalogModel();
                            catalogModel.setCatalogSeqNo(i);
                            catalogModel.setCatalogName(combineLine);
                            chapterModel.getLastCatalogModel().addChild(catalogModel);
                            continue;
                        }
                        if (regexUtil.isChapterLevel1(combineLine) || regexUtil.isChapterLevel1_2(lineArray, getBaseTable().getSynonyms())) {
                            //TODO 该代码需要调整到isChapterLevel1_2
                            double textTop = lineArray.get(0).getAsJsonObject().get("top").getAsDouble();
                            if (textTop > 150) {
                                logger.warn("分析PDF结构，发现疑似一级标题，该一级标题可能有问题，分析文本：{}，请确认！！！", lineArray.toString());
                                continue;
                            }
                            /**
                             *
                             *TODO 根据章节标题匹配表格体系名称，文本相似性分析，待补充该代码逻辑 update by wangyq 20181022
                             */
                            {
                                actualChapterModel = new ChapterModel();
                                actualChapterModel.setChapterTitle(combineLine);
                                actualChapterModel.setStartPage(pageModel.getPageNumber());
                                actualChapterModel.setTextLayout(createKVTextLayout(lineArray.get(0).getAsJsonObject(), pageModel.getPageNumber()));
                                actualChapterModel.setBottomPageNum(getPageModelBottomPageNum(pageModel));
                                documentModel.addActualChapterModel(actualChapterModel);
                            }
                            logger.info("分析PDF结构，根据正文章节标题获取对应表格体系标题，文本内容：{}。", combineLine);
                            TableItem tableItem = getTableItem(combineLine.replaceAll("\\s", ""));
                            if (tableItem == null) {
                                logger.error("分析PDF结构，根据正文章节标题获取对应表格体系标题失败，分析内容：{}。", combineLine);
                                continue;
                            }
                            previousChapterModel = chapterModel;
                            previousChapterModel.setEndPage(pageNumber - 1);
                            chapterModel = new ChapterModel();
                            chapterModel.setChapterSeqNo(pageNumber);//先用pagenumber代表序号，不过会跳页
                            chapterModel.setStartPage(pageNumber);
                            //TODO 默认获取0，可能会出现空值情况，后续再修改
                            chapterModel.setTextLayout(createKVTextLayout(lineArray.get(0).getAsJsonObject(), pageNumber));
                            chapterModel.setBottomPageNum(getPageModelBottomPageNum(pageModel));
                            chapterModel.setTableItem(tableItem);
                            chapterModel.setChapterTitle(combineLine);
                            documentModel.addChapterModel(chapterModel);
                            continue;
                        }
                        if (regexUtil.isChapterLevel2(combineLine)) {

                        }
                        if (isInner2(lineArray, tableAreaList)) {
                            continue;
                        } else {
                            String amt = regexUtil.queryThousandSeparator(combineLine);
                            if (!StringUtils.isEmpty(amt)) {
                                KVText vText = new KVText();
                                vText.setText(amt);
                                vText.setPageNumber(pageNumber);
                                vText.add(createKVTextLayout(lineArray.get(0).getAsJsonObject(), pageNumber));
                                documentModel.getNoThousandSeparatorList().add(vText);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            PDRectangle pdRectangle = pdDocument.getPage(i).getCropBox();
            pageModel.setHeight(pdRectangle.getHeight());
            pageModel.setWidth(pdRectangle.getWidth());
            chapterModel.addPageModel(pageModel);
            //TODO 先这样吧，后续再改善这里
            if (i == (documentModel.getTotalPageNumber() - 1)) {
                logger.info("分析PDF结构，已分析至文档末尾，在此采用比较粗暴的方式，把最后页码归属到表格体系最后章节，第{}页。", documentModel.getTotalPageNumber());
                chapterModel.setEndPage(documentModel.getTotalPageNumber());
            }
        }
        logger.info("分析PDF结构，输出目录结构......");
        documentModel.getChapterModels().forEach(chapter -> {
            if (chapter.getCatalogModels() != null && chapter.getCatalogModels().size() > 0) {
                logger.info("分析PDF结构，正文章节标题名称：{}，对应表格体系章节标题：{}，起始第：{}页，结束第：{}页，页码数量：{}页。", chapter.getChapterTitle(), chapter.getTableItem().getItemName(), chapter.getStartPage(), chapter.getEndPage(), chapter.getPageModelList().size());
                for (CatalogModel catalog : chapter.getCatalogModels()) {
                    //TODO 二级目录已解析，直接递归即可，后续添加二级目录输出
                    logger.info("分析PDF结构，一级目录名称：{}。", catalog.getCatalogName());
                }
            } else {
                logger.info("分析PDF结构，正文标题名称：{}，对应表格体系章节标题：{}，起始第：{}页，结束第：{}页，页码数量：{}页。", chapter.getChapterTitle(), chapter.getTableItem() == null ? null : chapter.getTableItem().getItemName(), chapter.getStartPage(), chapter.getEndPage(), chapter.getPageModelList().size());
            }
        });
        long t2 = System.currentTimeMillis();
        logger.info("分析PDF结构，PDF文档：{}目录结构已分析完成，耗时：{}毫秒，开始抽取业务模型基础指标......", documentModel.getPdfFilePath(), (t2 - t1));

        //TODO 后续优化
        logger.info("分析PDF结构，备注：{}。", "在这里想做成通用的数据抽取模式，可能会出现效率问题， 暂时先一一抽取吧，后续再改进优化！！！");

        /**
         * 目前现以M表为例，跑通M表的业务指标抽取
         */
        //涉及发行人、发行条款、主承销商、重要提示、目录结构，该项信息在第一页封皮位置，默认从此位置获取相应数据
        MTableExtractor mTableExtractor = null;
        try {
            //TODO 目前M、BM抽取引擎默认使用M抽取引擎，后续再想想如何拆分
            Map<String, Class<? extends MTableExtractor>> extractorMap = TableUtil.getInstance().initTable(tableType).createMTableExtractors();
            if (extractorMap.isEmpty()) {
                logger.error("分析PDF结构，表格类型：{}，表格体系列表为空，请检查！！！", tableType);
                return documentModel;
            }
            for (Iterator<Map.Entry<String, Class<? extends MTableExtractor>>> it = extractorMap.entrySet().iterator(); it.hasNext(); ) {
                Map.Entry<String, Class<? extends MTableExtractor>> entry = it.next();
                long t11 = System.currentTimeMillis();
                String tableId = entry.getKey();
                if (StringUtils.isEmpty(tableId)) {
                    continue;
                }
                String tableId2 = System.getProperty("tableId");
                if (StringUtils.isNotEmpty(tableId2)) {
                    if (!tableId2.equalsIgnoreCase(tableId)) {
                        continue;
                    }
                }
                logger.info("分析PDF结构，表格类型：{}，开始抽取表格体系第：{}章业务模型数据......", tableType, tableId);
                Class<? extends MTableExtractor> clazz = entry.getValue();
                try {
                    mTableExtractor = (MTableExtractor) Class.forName(clazz.getName()).newInstance();
                    documentModel.setTableType(tableType);
                    mTableExtractor.setDocumentModel(documentModel);
                    mTableExtractor.setBaseTable(getBaseTable());
                    mTableExtractor.setTableId(tableId);
                } catch (Exception e) {
                    logger.error("分析PDF结构，表格类型：{}，抽取第：{}章节内容发生错误：", tableType, tableId);
                    logger.error(e.getMessage(), e);
                }
                mTableExtractor.extract();
                long t12 = System.currentTimeMillis();
                logger.info("分析PDF结构，表格类型：{}，表格体系第：{}章业务模型数据抽取完成，耗时：{}毫秒。", tableType, tableId, (t12 - t11));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        closeDocument(pdDocument);
        long t3 = System.currentTimeMillis();
        logger.info("分析PDF结构，表格类型：{}，PDF文档：{}分析完成，总计耗时：{}毫秒。", tableType, pdfFile.getAbsolutePath(), (t3 - t1));
        return documentModel;
    }

    protected TextLayout createKVTextLayout(JsonObject jsonObject, int pageNumber) {
        double top = jsonObject.get("top").getAsDouble();
        double left = jsonObject.get("left").getAsDouble();
        double height = jsonObject.get("height").getAsDouble();
        double width = jsonObject.get("width").getAsDouble();
        return new TextLayout(jsonObject.get("text").getAsString(), pageNumber, top, left, height, width);
    }

    protected boolean isInner2(JsonArray itemArray, List<TableArea> tableAreaList) {
        if (CollectionUtils.isEmpty(tableAreaList)) {
            return false;
        }
        boolean isInner = false;
        for (int i = 0; i < itemArray.size(); i++) {
            JsonObject jsonObject = itemArray.get(i).getAsJsonObject();
            double height = jsonObject.get("height").getAsDouble();
            double top = jsonObject.get("top").getAsDouble();
            double left = jsonObject.get("left").getAsDouble();
            if (top == 0 && left == 0) {
                continue;
            }
            for (int j = 0; j < tableAreaList.size(); j++) {
                TableArea tableArea = tableAreaList.get(j);
                if (top > tableArea.getTop() && (top + height) < (tableArea.getTop() + tableArea.getHeight())) {
                    isInner = true;
                    break;
                }
            }
        }
        return isInner;
    }

    protected List<TableArea> recognizePageTableRegion(PageModel pageModel) {
        List<TableArea> tableAreaList = new ArrayList<>();
        try {
            List<String> pageTableDataList = pageModel.getTableDataList();//extractPdfByTable(new String[]{documentModel.getPdfFilePath(), "-l", "-p", String.valueOf(pageModel.getPageNumber()), "-a", pageArea.getLayout(), "-f", "JSON"});
            if (CollectionUtils.isEmpty(pageTableDataList)) {
                return null;
            }
            for (int i = 0; i < pageTableDataList.size(); i++) {
                String tableJsonData = pageTableDataList.get(i);
                JsonArray tableDataArray = str2Json(tableJsonData).getAsJsonArray();
                JsonObject tableData = tableDataArray.get(0).getAsJsonObject();
                double top = tableData.get("top").getAsDouble();
                double left = tableData.get("left").getAsDouble();
                double width = tableData.get("width").getAsDouble();
                double height = tableData.get("height").getAsDouble();
                TableArea tableArea = new TableArea(pageModel.getPageNumber(), top, left, width, height);
                tableAreaList.add(tableArea);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return tableAreaList;
    }

    protected JsonElement str2Json(String jsonString) {
        return gsonUtil.parse(jsonString);
    }

    private GsonUtil gsonUtil = GsonUtil.getInstance();
}
